package com.baofu.paycore.biz.paycore.handler.impl;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.baofu.account.service.facade.model.request.UnFreezeTransferReqDTO;
import com.baofu.clear.service.facade.model.WithdrawReqDTO;
import com.baofu.clear.service.facade.model.resp.NotifyDTO;
import com.baofu.paycore.biz.engine.result.CommandResult;
import com.baofu.paycore.biz.external.account.AccountService;
import com.baofu.paycore.biz.external.account.converter.AccountRequestConverter;
import com.baofu.paycore.biz.external.clear.ClearQueryService;
import com.baofu.paycore.biz.external.clear.ClearWithdrawService;
import com.baofu.paycore.biz.external.clear.converter.ClearRequestConverter;
import com.baofu.paycore.biz.external.clear.model.ClearFundResBO;
import com.baofu.paycore.biz.model.BackPayBO;
import com.baofu.paycore.biz.paycore.converter.BO2BOConverter;
import com.baofu.paycore.biz.paycore.handler.BaseHandler;
import com.baofu.paycore.common.enums.FundTypeEnum;
import com.baofu.paycore.common.exception.PayCoreBizException;
import com.baofu.paycore.manager.converter.BO2DOConverter;
import com.baofu.paycore.manager.model.AccountRespBO;
import com.baofu.paycore.manager.model.ResponseBO;
import com.baofu.paycore.manager.model.UnFreezeTransferRespBO;
import com.baofu.paycore.manager.model.UnFreezeWithdrawRespBO;
import com.baofu.paycore.manager.model.withdraw.WithdrawBO;
import com.baofu.paycore.manager.model.withdraw.WithdrawBankBO;
import com.baofu.paycore.manager.model.withdraw.WithdrawFundBO;
import com.baofu.paycore.manager.storage.impl.ClearAccountManager;
import com.baofu.paycore.manager.storer.WithdrawOrderRepository;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.baofu.paycore.service.facade.enums.ServiceCodeEnum;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 解冻提现处理
 * <p>
 * </p>
 * User:ames wang  Date: 2017/5/25
 */
@Slf4j
@Service
public class UnfreezeWithdrawHandler extends BaseHandler<WithdrawBO,NotifyDTO> {

    @Autowired
    private WithdrawOrderRepository  withdrawOrderRepository;

    /**
     * 清分提现服务
     */
    @Autowired
    private ClearWithdrawService clearWithdrawService;

    @Value("${withdraw.unFreeze.time}")
    private Integer unfreezeTime;

    /**
     * 账务转账服务
     */
    @Autowired
    private AccountService accountService;

    /**
     * 清分查询服务
     */
    @Autowired
    private ClearQueryService clearQueryService;

    /**
     * 提现资金单服务
     */
    @Autowired
    private ClearAccountManager clearAccountManager;

    @Override
    public WithdrawBO activeOrder(String orderId) {
        WithdrawBO withdrawBO = withdrawOrderRepository.load(orderId);
        if (withdrawBO == null) {
            log.error("找不到提现订单:{}", orderId);
            throw new PayCoreBizException(PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
        }
        return withdrawBO;    }

    @Override
    public WithdrawBO queryOrderByPayNo(String payNo) {
        WithdrawBO withdrawBO = withdrawOrderRepository.loadByPayNo(payNo);
        if (withdrawBO == null) {
            log.error("找不到提现订单:{}", payNo);
            throw new PayCoreBizException(PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
        }
        return withdrawBO;
    }

    @Override
    protected void checkBeforeProcess(WithdrawBO order) {

    }

    @Override
    protected void processOrders(WithdrawBO order) {

    }

    @Override
    protected void reStore(WithdrawBO order) {

    }

    @Override
    protected void processAfter(WithdrawBO order, Map<String, CommandResult> cmdResult) {

    }

    /**
     * 需重试接口 对内部资金单的重试（订单状态推进）
     *
     * @param withdrawBO  提现订单对象
     * @return ResponseBO
     * @throws RuntimeException 出现异常时表示重试没有成功（子类方法实现中不可捕获异常）
     */
    public ResponseBO dalInnerFundProc(WithdrawBO withdrawBO) throws RuntimeException {
        ResponseBO responseBO = null;
        List<WithdrawFundBO> withdrawFundBOs = withdrawBO.getWithdrawFundBO();
        if(ServiceCodeEnum.ISSUE_WITHDRAW.getCode().equals(withdrawBO.getServiceCode())){
            WithdrawBankBO withdrawBankBO = withdrawBO.getWithdrawBankBO();
            if (PayStatusEnums.FAIL.getCode().equals(withdrawBankBO.getStatus())){
                log.error("解冻提现重试接口 单号:{}, 冻结单状态:{}", withdrawBankBO.getPayNo(), withdrawBankBO.getStatus());
                return new ResponseBO(withdrawBO.getStatus(), withdrawBO.getErrorCode(),
                        withdrawBO.getErrorMsg(), withdrawBO.getPayNo(), withdrawBO.isRealFlag());
            }
        }
        //账务重试开始
        for (WithdrawFundBO withdrawFundBO : withdrawFundBOs){
            //校验
            if (!validFundStatus( withdrawFundBO)) continue;

            FundTypeEnum fundType = FundTypeEnum.explain(withdrawFundBO.getFundType());
            if (null == fundType) continue;

            switch (fundType){
                case UNFREEZE_WITHDRAW:
                    responseBO = retryUnfreezeWithdraw(withdrawBO, withdrawFundBO);
                    break;
                case FEE:
                    responseBO = retryUnfreezeTransfer(withdrawBO, withdrawFundBO);
                    break;
                default:
                    log.error("暂不支持此业务 ：{}", fundType);
                    throw new PayCoreBizException(PayCoreErrorCode.NOT_IN_SERVICE);
            }
            if (null == responseBO){
                log.error("解冻提现重试 资金单类型:{} 资金单号:{} 资金单状态:{}", withdrawFundBO.getFundType(),
                        withdrawFundBO.getFundDetailNo(), withdrawFundBO.getStatus());
                break;
            }

            if (!validateContinue(responseBO.isRealFlag(), responseBO.getStatus())){
                log.error("解冻提现重试失败 请求服务类型:{} 资金单号:{} 资金单类型:{} 转账处理状态:{}", withdrawBO.getServiceCode(),
                        responseBO.getRequestNo(), withdrawFundBO.getFundType(), responseBO.getStatus());
                break;
            }
        }
        //处理主单状态
        handleUnfreezeStatus(withdrawBO);
        this.reStoreUnfreeze(withdrawBO);
        return responseBO;
    }

    /**
     * 解冻提现
     *
     * @param withdrawBO    提现单
     * @param maps          集合
     */
    private void unFreezeWithdraw(WithdrawBO withdrawBO, Map<String, WithdrawFundBO> maps, String eventNo) {
        //获取解冻提现单
        WithdrawFundBO unFreezeWithdraw = maps.get(FundTypeEnum.UNFREEZE_WITHDRAW.getCode());
        if (StringUtils.isNotEmpty(eventNo)) unFreezeWithdraw.setEventNo(eventNo);
        //调用账务解冻提现
        UnFreezeWithdrawRespBO respBO = accountService.unFreezeWithdraw(withdrawBO, unFreezeWithdraw);
        this.unFreezeWithdrawResult(withdrawBO, respBO);
        if(!Objects.equals(PayStatusEnums.SUCCESS.getCode(),respBO.getStatus())){
            log.debug("解冻提现未成功:{}",respBO.getStatus());
            return;
        }

        //解冻转账
        WithdrawFundBO unFreezeTransfer = maps.get(FundTypeEnum.FEE.getCode());
        if (unFreezeTransfer == null) return;
       /* //冻结码赋值
        String freezeCode=redisManager.queryObjectByKey(RedisKeyUtil.buildRedisKey(RedisKeyEnum.WITHDRAW_FREEZE_CODE, withdrawBO.getRequestBizNo(),
                withdrawBO.getRequestOrderNo(), withdrawBO.getRequestSystem()),String.class);
        unFreezeTransfer.setFreezeCode(freezeCode)*/
        //手续费解冻转账
        this.unFreezeTransfer(withdrawBO, unFreezeTransfer);
    }

    /**
     * 解冻提现结果处理
     *
     * @param order     提现单
     * @param respBO    解冻提现响应
     */
    private void unFreezeWithdrawResult(WithdrawBO order, UnFreezeWithdrawRespBO respBO) {
        order.setRealFlag(respBO.isRealRtn());
        //获取解冻提现单
        WithdrawFundBO withdrawFundBO = order.getWithdrawFund(order.getWithdrawFundBO(),
                FundTypeEnum.UNFREEZE_WITHDRAW.getCode());
        BO2BOConverter.unFreezeWithdrawResult(withdrawFundBO, respBO);
    }

    /**
     * 解冻转账处理
     *
     * @param withdrawBO        提现单
     * @param withdrawFundBO    提现资金单
     */
    private void unFreezeTransfer(WithdrawBO withdrawBO, WithdrawFundBO withdrawFundBO){
        UnFreezeTransferReqDTO transferReqDTO =
                AccountRequestConverter.getUnFreezeTransferByFundBO(withdrawFundBO, withdrawBO);
        //调账务解冻转账
        UnFreezeTransferRespBO respBO = accountService.unFreezeTransfer(withdrawBO.getPayNo(),
                withdrawBO.getServiceCode(), transferReqDTO);
        //响应结果处理
        unFreezeTransferResult(withdrawBO, respBO);
    }

    /**
     * 解冻转账单结果
     *
     * @param order     提现单
     * @param respBO    解冻转账单结果
     */
    private void unFreezeTransferResult(WithdrawBO order,UnFreezeTransferRespBO respBO){
        order.setRealFlag(respBO.isRealRtn());
        //获取手续费单
        WithdrawFundBO withdrawFundBO = order.getWithdrawFund(order.getWithdrawFundBO(),
                FundTypeEnum.FEE.getCode());
        BO2BOConverter.unFreezeTransferResult(withdrawFundBO, respBO);
    }

    /**
     * 解冻提现指令
     * 解冻之前冻结的账户
     * @param order 单据
     */
    protected void processUnfreezeOrders(WithdrawBO order) {

        Map<String, WithdrawFundBO> maps = this.getWithdrawFund(order);
        if (order.getWithdrawBankBO() != null) {
            //调用清分提现
            WithdrawBankBO withdrawBankBO = order.getWithdrawBankBO();
            if (!clearWithdraw(order, withdrawBankBO)) return;
        }
        //1.清分提现明确成功,做账务解冻提现,解冻转账服务
        unFreezeWithdraw(order, maps, null);
        //4.单状态处理
        handleUnfreezeStatus(order);
    }
    /**
     * 资金单集合转换
     *
     * @param withdrawBO    提现单
     * @return              map
     */
    private Map<String, WithdrawFundBO> getWithdrawFund(WithdrawBO withdrawBO) {
        Map<String, WithdrawFundBO> map = Maps.newHashMap();
        for (WithdrawFundBO fundBO : withdrawBO.getWithdrawFundBO()) {
            map.put(fundBO.getFundType(), fundBO);
        }
        return map;
    }

    private void handleUnfreezeStatus(WithdrawBO withdrawBO) {
        List<WithdrawFundBO> withdrawFundBOs = withdrawBO.getWithdrawFundBO();
        if (CollectionUtils.isEmpty(withdrawFundBOs)){
            return;
        }
        if(ServiceCodeEnum.ISSUE_WITHDRAW.getCode().equals(withdrawBO.getServiceCode())){
            //银行单状态
            String bankStatus = withdrawBO.getWithdrawBankBO().getStatus();
            withdrawBO.setStatus(bankStatus);
        }else {
            withdrawBO.setStatus(PayStatusEnums.SUCCESS.getCode());
        }
        //针对提现业务,银行提现结果已经成功了,为不引起资金损失,一定是要账务成功的
        //若账务调用返回失败,给上层处理中,后面大总管确认后手动去补,使账务成功
        for (WithdrawFundBO withdrawFundBO : withdrawFundBOs) {
            if (PayStatusEnums.FAIL.getCode().equals(withdrawFundBO.getStatus())) {
                withdrawBO.setStatus(PayStatusEnums.INIT.getCode());
                withdrawBO.setErrorCode(withdrawFundBO.getErrorCode());
                withdrawBO.setErrorMsg(withdrawFundBO.getErrorReason());
                break;
            }
            if (PayStatusEnums.INIT.getCode().equals(withdrawFundBO.getStatus())){
                withdrawBO.setStatus(PayStatusEnums.INIT.getCode());
                break;
            }
        }
    }

    /**
     * 解冻提现服务类
     *
     * @param withdrawBO    提现参数
     * @return              返回值
     */
    protected WithdrawBO doUnfreezeExecute(WithdrawBO withdrawBO){
        checkBeforeProcess(withdrawBO);
        processUnfreezeOrders(withdrawBO);
        reStoreUnfreeze(withdrawBO);
        return withdrawBO;
    }
    /**
     * 持久化对象
     *
     * @param order 单据
     */
    protected void reStoreUnfreeze(WithdrawBO order) {
        withdrawOrderRepository.update(order);
    }

    /**
     * 解冻提现处理
     *
     * @param withdrawBO    提现参数
     * @return              返回值
     */
    public BackPayBO processUnfreeze(WithdrawBO withdrawBO) {
        log.debug("解冻提现开始处理：{}", withdrawBO);
        doUnfreezeExecute(withdrawBO);
        log.info("解冻提现完成结果：{}",withdrawBO);
        return BO2BOConverter.withdrawBO2BackPayBO(withdrawBO);
    }

    /**
     * 清分提现处理
     *
     * @param withdrawBO     提现单
     * @param withdrawBankBO 提现银行单
     * @return true:提现成功;false:提现失败
     */
    private boolean clearWithdraw(WithdrawBO withdrawBO, WithdrawBankBO withdrawBankBO) {

        WithdrawReqDTO withdrawReqDTO = ClearRequestConverter.convertToWithdrawDTO(withdrawBO, withdrawBankBO);
        ClearFundResBO clearFundResBO = clearWithdrawService.withdraw(withdrawReqDTO, ServiceCodeEnum.ISSUE_WITHDRAW.getCode());
        //结果集处理
        this.bankResult(withdrawBO, clearFundResBO);

        if (clearFundResBO.isRealFlag() && PayStatusEnums.FAIL.getCode().equals(clearFundResBO.getPayStat())) {
            //银行单失败,其他未处理资金单也改为失败
            bankFailProcessor(withdrawBO);
        }

        return clearFundResBO.isRealFlag() && PayStatusEnums.SUCCESS.getCode().equals(clearFundResBO.getPayStat());
    }

    /**
     * 银行单失败处理
     *
     * @param order          订单
     * @param clearFundResBO 银行处理结果
     */
    private void bankResult(WithdrawBO order, ClearFundResBO clearFundResBO) {
        order.setRealFlag(clearFundResBO.isRealFlag());
        order.getWithdrawBankBO().setStatus(clearFundResBO.getPayStat());
        order.getWithdrawBankBO().setTransDate(clearFundResBO.getResponseDate());
        order.getWithdrawBankBO().setErrorCode(clearFundResBO.getErrorCode());
        order.getWithdrawBankBO().setErrorReason(clearFundResBO.getErrorMsg());
        order.getWithdrawBankBO().setTransReceiptNo(clearFundResBO.getResponseNo());
        order.setErrorCode(clearFundResBO.getErrorCode());
        order.setErrorMsg(clearFundResBO.getErrorMsg());
        order.getWithdrawFund(order.getWithdrawFundBO(),
                FundTypeEnum.UNFREEZE_WITHDRAW.getCode()).setEventNo(clearFundResBO.getEventNo());
    }

    /**
     * 请求银行单失败处理状态
     *
     * @param withdrawBO 提现单
     */
    private void bankFailProcessor(WithdrawBO withdrawBO) {
        withdrawBO.setStatus(PayStatusEnums.FAIL.getCode());
        withdrawBO.getWithdrawFundBO().forEach(withdrawFundBO -> {
                    if (!FundTypeEnum.WITHDRAW_FREEZE.getCode().equals(withdrawFundBO.getFundType())) {
                        withdrawFundBO.setStatus(PayStatusEnums.FAIL.getCode());
                    }
                }
        );
    }

    /**
     * 校验资金单状态
     *
     * @param withdrawFundBO    提现资金单
     * @return                  true;false
     */
    private boolean validFundStatus(WithdrawFundBO withdrawFundBO) {
        if (PayStatusEnums.SUCCESS.getCode().equals(withdrawFundBO.getStatus())) {
            log.debug("解冻提现重试 资金单类型:{} 资金单号:{} 资金单状态:{}", withdrawFundBO.getFundType(),
                    withdrawFundBO.getFundDetailNo(), withdrawFundBO.getStatus());
            return false;
        }
        if (PayStatusEnums.FAIL.getCode().equals(withdrawFundBO.getStatus())) {
            log.error("提现重试 资金单类型:{} 资金单号:{} 资金单状态:{}", withdrawFundBO.getFundType(),
                    withdrawFundBO.getFundDetailNo(), withdrawFundBO.getStatus());
            return false;
        }

        return true;
    }

    /**
     * 重试解冻提现单
     *
     * @param withdrawBO        提现主单
     * @param withdrawFundBO    解冻提现单
     * @return                  ResponseBO
     */
    private ResponseBO retryUnfreezeWithdraw(WithdrawBO withdrawBO, WithdrawFundBO withdrawFundBO){

        UnFreezeWithdrawRespBO respBO = accountService.unFreezeWithdraw(withdrawBO, withdrawFundBO);
        BO2BOConverter.unFreezeWithdrawResult(withdrawFundBO, respBO);

        return getResponseBO(respBO, withdrawFundBO);
    }

    /**
     * 重试解冻转账单
     *
     * @param withdrawBO        提现主单
     * @param withdrawFundBO    解冻提现单
     * @return                  ResponseBO
     */
    private ResponseBO retryUnfreezeTransfer(WithdrawBO withdrawBO, WithdrawFundBO withdrawFundBO){
        UnFreezeTransferReqDTO transferReqDTO =
                AccountRequestConverter.getUnFreezeTransferByFundBO(withdrawFundBO, withdrawBO);
        //调账务解冻转账
        UnFreezeTransferRespBO respBO = accountService.unFreezeTransfer(withdrawBO.getPayNo(),
                withdrawBO.getServiceCode(), transferReqDTO);

        return getResponseBO(respBO, withdrawFundBO);
    }

    private ResponseBO getResponseBO(AccountRespBO respBO, WithdrawFundBO withdrawFundBO){

        clearAccountManager.modifyOrder(BO2DOConverter.getClearAccountByFundBO(withdrawFundBO));

        return new ResponseBO(respBO.getStatus(), respBO.getErrorCode(),respBO.getErrorMsg(), respBO.getAccDate(),
                respBO.getReceiptNo(), withdrawFundBO.getFundDetailNo(), respBO.getFreezeCode(), respBO.isRealRtn());
    }

    /**
     * 提现单状态推进
     *
     * @param withdrawBO    提现单
     */
    public void advance(WithdrawBO withdrawBO, String eventNo) {
        Map<String,WithdrawFundBO> maps = this.getWithdrawFund(withdrawBO);
        //解冻提现
        this.unFreezeWithdraw(withdrawBO, maps, eventNo);

        handleUnfreezeStatus(withdrawBO);
    }

    /**
     * 提现清分超时处理
     *
     * @param requestOrderNo    请求单号
     * @return                  Response
     */
    public ResponseBO withdrawTimeOut(WithdrawBO withdrawBO, String requestOrderNo) {
        ClearFundResBO clearFundResBO = clearQueryService.queryWithdrawByRequestOrderNo(withdrawBO.getPayNo(),
                requestOrderNo);
        //清分返回失败,支付单处理失败,冻结提现单需解冻处理
        if (clearFundResBO.isRealFlag() && PayStatusEnums.FAIL.getCode().equals(clearFundResBO.getPayStat())) {
            if (checkClearAmt(withdrawBO.getAmt(), clearFundResBO.getAmt())){
                return new ResponseBO(PayStatusEnums.INIT.getCode(), PayCoreErrorCode.AMT_IS_NOT_IN_CONFORMITY.getErrorCode(),
                        PayCoreErrorCode.AMT_IS_NOT_IN_CONFORMITY.getErrorDesc(), new Date(), null, requestOrderNo, false);
            }
            this.failFundProcess(withdrawBO,true);
            reStoreUnfreeze(withdrawBO);
            return new ResponseBO(PayStatusEnums.FAIL.getCode(), clearFundResBO.getErrorCode(),
                    clearFundResBO.getErrorMsg(), new Date(), null, requestOrderNo, true);
        }
        //清分返回成功,走后面流程
        if (clearFundResBO.isRealFlag() && PayStatusEnums.SUCCESS.getCode().equals(clearFundResBO.getPayStat())) {
            if (checkClearAmt(withdrawBO.getAmt(), clearFundResBO.getAmt())){
                return new ResponseBO(PayStatusEnums.INIT.getCode(), PayCoreErrorCode.AMT_IS_NOT_IN_CONFORMITY.getErrorCode(),
                        PayCoreErrorCode.AMT_IS_NOT_IN_CONFORMITY.getErrorDesc(), new Date(), null, requestOrderNo, false);
            }
            //更新清分提现单状态
            withdrawBO.getWithdrawBankBO().setStatus(PayStatusEnums.SUCCESS.getCode());
            //推进订单
            advance(withdrawBO, clearFundResBO.getEventNo());
            reStoreUnfreeze(withdrawBO);
            return new ResponseBO(withdrawBO.getStatus(), withdrawBO.getErrorCode(),
                    withdrawBO.getErrorMsg(), clearFundResBO.getResponseDate(), clearFundResBO.getResponseNo(),
                    requestOrderNo, true);
        }

        return new ResponseBO(PayStatusEnums.INIT.getCode(), clearFundResBO.getErrorCode(),
                clearFundResBO.getErrorMsg(), clearFundResBO.getResponseDate(), clearFundResBO.getResponseNo(),
                requestOrderNo, false);
    }

    private boolean checkClearAmt(Long withdrawAmt, Long clearAmt) {

        return !withdrawAmt.equals(clearAmt);
    }

    /**
     * 失败流程处理
     *  冻结账户失败了,主单和其他资金单全是失败
     * @param withdrawBO    提现单
     * @param afterClear    是否在清分流程后 清分流程后的单子不处理冻结单,由异步任务处理异常解冻流程
     */
    private void failFundProcess(WithdrawBO withdrawBO,boolean afterClear){
        withdrawBO.setStatus(PayStatusEnums.FAIL.getCode());
        withdrawBO.getWithdrawBankBO().setStatus(PayStatusEnums.FAIL.getCode());
        for (WithdrawFundBO rechargeFundBO : withdrawBO.getWithdrawFundBO()){
            if(afterClear&&rechargeFundBO.getFundType().equals(FundTypeEnum.WITHDRAW_FREEZE.getCode())){
                continue;
            }
            rechargeFundBO.setStatus(PayStatusEnums.FAIL.getCode());
        }
    }
}
